﻿using Project362.Users;
using Project362.Collections;
using Project362.Files;
using Project362.Tags;
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace Project362
{
    public class Controller
    {

        #region Variables

        /// <summary>
        /// An object that handles all users. MODEL CLASS
        /// </summary>
        private UserHandler userHandler;

        /// <summary>
        /// A handler for all collections. MODEL CLASS
        /// </summary>
        private CollectionHandler collectionHandler;
		
		/// <summary>
		/// A handler for all tags. MODEL CLASS
		/// </summary>
		private TagHandler tagHandler;

        #endregion
        #region Constructors

        /// <summary>
        /// Creates a new System.
        /// </summary>
        public Controller()
        {
            this.userHandler = new UserHandler(Directory.GetCurrentDirectory() + "\\Users.bin");
            userHandler.CurrentUser = null;//implies no user is logged in
            this.collectionHandler = new CollectionHandler(Directory.GetCurrentDirectory() + "\\Collections.bin");
			this.tagHandler=new TagHandler(Directory.GetCurrentDirectory()+"\\Tags.bin");
        }

        #endregion
        #region Public Methods

        /// <summary>
        /// Attempts to log in the user with the specified user name and password.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <param name="password">Password.</param>
        /// <returns>True if logged in, false otherwise.</returns>
        public bool login(string userName, string password)
        {
            if (userHandler.CurrentUser != null)
                return false;
            userHandler.CurrentUser = userHandler.Login(userName, password);
            return userHandler.CurrentUser != null;
        }

        /// <summary>
        /// Logs the currently logged in user out.
        /// </summary>
        /// <returns>True if successfully logged out, false otherwise.</returns>
        public bool logout()
        {
            if (userHandler.CurrentUser != null)
            {
                userHandler.CurrentUser = null;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Attempts to add a new user to the system.
        /// </summary>
        /// <param name="userName">The new user name.</param>
        /// <param name="password">The new user password.</param>
        /// <param name="level">The new user permission level.</param>
        /// <returns>True if successfull, false otherwise.</returns>
        public bool AddUser(string userName, string password, PermissionLevel level)
        {
            if (userHandler.NumUsers > 0 && userHandler.CurrentUser == null || userHandler.CurrentUser != null && userHandler.CurrentUser.Permissions < PermissionLevel.tier4)
                return false;
            return userHandler.AddUser(userName, password, level);
        }

        /// <summary>
        /// Attempts to remove the user with the given name from the system.
        /// </summary>
        /// <param name="userName">The user name.</param>
        /// <returns>True if successfull, false otherwise.</returns>
        public bool RemoveUser(string userName)
        {
            if (userHandler.CurrentUser == null || userHandler.CurrentUser.Permissions < PermissionLevel.tier4)
                return false;
            return userHandler.RemoveUser(userName);
        }

        /// <summary>
        /// Attempts to change the user permissions of the user specified by the given name to the given
        /// permission level.
        /// </summary>
        /// <param name="userName">The name of the account to change.</param>
        /// <param name="level">The new permission level.</param>
        /// <returns>True if successfull, false otherwise.</returns>
        public bool ChangeUserPermissions(string userName, PermissionLevel level)
        {
            if (userHandler.CurrentUser == null || userHandler.CurrentUser.Permissions < PermissionLevel.tier4)
                return false;
            return userHandler.ChangeUserPermissions(userName, level);
        }
        public int getNumberOfUsers()
        {
            return userHandler.NumUsers;
        }
        public PermissionLevel getPermissionContext()
        {
            return userHandler.CurrentUser.Permissions;
        }
        /// <summary>
        /// Adds a file to a specific collection.
        /// </summary>
        /// <param name="filename">the name of the file to add</param>
        /// <param name="collection">the name of the collection to add it to</param>
        /// <returns>true if success, false otherwise</returns>
        public bool addFileToCollection(string name, string location, string collection)
        {
            return collectionHandler.addFileToCollection(name, location, collection);
        }
        /// <summary>
        /// Removes a file from a collection, if it exists there
        /// </summary>
        /// <param name="filename">The name of the file to remove</param>
        /// <param name="collection">The collection to remove it from</param>
        /// <returns>true if success, false otherwise</returns>
        public bool removeFileFromCollection(string collection, string name)
        {
            return collectionHandler.removeFileFromCollection(collection, name);
        }
        /// <summary>
        /// Gets the files in a collection
        /// </summary>
        /// <param name="collection">The collection to access</param>
        /// <returns>true if success, false otherwise</returns>
        public List<FileClass> getFilesInCollection(string collection)
        {
            return collectionHandler.getFilesInCollection(collection);
        }
        /// <summary>
        /// Moves a file from one collection to another
        /// </summary>
        /// <param name="filename">The file to move</param>
        /// <param name="sourcec">The collection the file is currently in</param>
        /// <param name="destc">The collection to move to</param>
        /// <returns>true if success, false otherwise</returns>
        public bool moveFileBetweenCollections(string sourcec, string name, string destc)
        {
            return collectionHandler.moveFileBetweenCollections(sourcec, name, destc);
        }
        /// <summary>
        /// Check the filesystem to see if a certain file exists
        /// </summary>
        /// <param name="filename">Filename to check</param>
        /// <returns>true if success, false otherwise</returns>
        public bool checkFileExists(string filelocation)
        {
            return File.Exists(filelocation);
        }
        /// <summary>
        /// Calculate the md5 sum for a file
        /// </summary>
        /// <param name="filename">filename to check</param>
        /// <returns>true if success, false otherwise</returns>
        public string checksumFile(string collection, string name)
        {
            System.Security.Cryptography.MD5 m = System.Security.Cryptography.MD5.Create();
            FileStream f = System.IO.File.OpenRead(collectionHandler.getCollection(collection).getFile(name).Filename);
            byte[] data = m.ComputeHash(f);
            StringBuilder s = new StringBuilder();
            foreach (byte datum in data)
            {
                s.Append(datum.ToString("x2"));
            }
            return s.ToString();
        }
        /// <summary>
        /// Adds a new collection to the system
        /// </summary>
        /// <param name="collection">name of the new collection</param>
        /// <returns>true if success, false otherwise</returns>
        public bool addNewCollection(string collection)
        {
            return collectionHandler.addNewCollection(collection);
        }
        /// <summary>
        /// Removes an existing collection from the system
        /// </summary>
        /// <param name="collection">name of collection to remove</param>
        /// <returns>true if success, false otherwise</returns>
        public bool removeCollection(string collection)
        {
            return collectionHandler.removeCollection(collection);
        }
        /// <summary>
        /// Saves all handler data (except user)
        /// </summary>
        /// <returns>true if success, false otherwise</returns>
        public bool SaveAll()
        {
            return this.collectionHandler.saveCollections() && this.tagHandler.saveTags();
        }

        public bool OpenFile(string col, string name)
        {
            Collection temp = collectionHandler.getCollection(col);
            if (temp != null)
                return temp.getFile(name).Open();
            return false;
        }

        public bool CopyFiles(string col, string dest)
        {
            if(!dest.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))dest+=System.IO.Path.DirectorySeparatorChar;
            Collection temp = collectionHandler.getCollection(col);
            if (temp != null)
            {
                foreach (FileClass f in temp.getFiles())
                {
                    f.Copy(dest + col + System.IO.Path.DirectorySeparatorChar);
                }
            }
            return false;
        }

        public bool DeleteFile(string col, string file)
        {
            Collection temp = collectionHandler.getCollection(col);
            if (temp != null)
                return temp.getFile(file).Delete(temp);
            return false;
        }

        public bool MoveFile(string col, string name, string dest)
        {
            Collection temp = collectionHandler.getCollection(col);
            if (temp != null)
                return temp.getFile(name).MoveFile(dest);
            return false;
        }

        public bool ConvertFile(string col, string file, string format)
        {
            Collection temp = collectionHandler.getCollection(col);
            if (temp != null)
                return temp.getFile(file).Convert(format);
            return false;
        }
		public bool addTag(string tag){
			return this.tagHandler.addTag(tag);
		}
		public bool removeTag(string tag){
			Tag t=this.tagHandler.getTag(tag);
			foreach(FileClass f in t.getFiles()){
				f.removeTag(t);
			}
			return this.tagHandler.removeTag(tag);
		}
		public bool addTagToFile(string collection, string file, string tag){
			Tag t=this.tagHandler.getTag(tag);
			Collection c= this.collectionHandler.getCollection(collection);
			FileClass f=c.getFile(file);
			if(t!=null&&f!=null){
				return t.addFile(f)&&f.addTag(t);
			} else return false;
		}
		public bool removeTagFromFile(string collection, string file, string tag){
			Tag t=this.tagHandler.getTag(tag);
			Collection c= this.collectionHandler.getCollection(collection);
			FileClass f=c.getFile(file);
			if(t!=null&&f!=null){
				return t.removeFile(f)&&f.removeTag(t);
			} else return false;
		}
		public List<FileClass> getFilesWithTag(string tag){
			return this.tagHandler.getTag(tag).getFiles();
		}
		public List<Tag> getListOfTags(){
			return this.tagHandler.getList();
		}
        public List<Collection> getListofallCollections()
        {
	        return this.collectionHandler.getListofallCollections();
        }
        public bool addAttributetoFile(string collection, string file, string name, string content)
        {
	        return this.collectionHandler.addAttributetoFile(collection, file, name, content);
        }
        public bool editAttributeContent(string collection, string file, string name, string content)
        {
	        return this.collectionHandler.editAttributeContent(collection,file, name, content);
        }
        public bool removeAttributefromFile(string collection, string file, string name)
        {
	        return this.collectionHandler.removeAttributefromFile(collection, file, name);
        }
        public List<FileClass> sortByDirectory(List<FileClass> input)
        {
            MergeSort<FileClass> m = new MergeSort<FileClass>(new DirectoryComparer());
            return m.Sort(input);
        }
        public List<FileClass> sortByName(List<FileClass> input)
        {
            MergeSort<FileClass> m = new MergeSort<FileClass>(new NameComparer());
            return m.Sort(input);
        }
        public List<FileClass> sortByCollection(List<FileClass> input)
        {
            MergeSort<FileClass> m = new MergeSort<FileClass>(new CollectionComparer());
            return m.Sort(input);
        }
        public List<FileClass> sortByFiletype(List<FileClass> input)
        {
            MergeSort<FileClass> m = new MergeSort<FileClass>(new FiletypeComparer());
            return m.Sort(input);
        }
        public List<FileClass> sortByTags(List<FileClass> input)
        {
            MergeSort<FileClass> m = new MergeSort<FileClass>(new TagsComparer());
            return m.Sort(input);
        }
        public List<FileClass> searchForTag(string tag)
        {
            // No tag?! Screw you!
            if (tag == null)
                return null;

            Tag t = tagHandler.getTag(tag);

            // Oops, no tag.
            if (t == null)
                return null;

            return t.getFiles();
        }
        public List<FileClass> searchForFilename(string name)
        {
            // No name?!  Screw you!
            if (name == null)
                return null;

            List<Collection> colList = collectionHandler.getListofallCollections();

            // No collections.. interesting...
            if (colList == null)
                return null;

            List<FileClass> ret = new List<FileClass>();
            foreach (Collection c in colList)
            {
                FileClass fc = c.getFile(name);
                if (fc != null)
                    ret.Add(fc);
            }

            return ret;
        }
        public List<FileClass> searchForCollection(string name)
        {
            // No name.. I see how it is.
            if (name == null)
                return null;

            Collection c = collectionHandler.getCollection(name);

            // Couldn't find the collection, sorry.
            if (c == null)
                return null;

            return c.getFiles();
        }
        public List<FileClass> searchForMeta(string key, string value)
        {
            if (key == null || value == null)
                return null;

            List<Collection> cols = collectionHandler.getListofallCollections();

            if (cols == null)
                return null;

            List<FileClass> ret = new List<FileClass>();
            foreach (Collection c in cols)
            {
                List<FileClass> temp = c.getFilesWithMeta(key, value);
                if (temp != null)
                    ret.AddRange(temp);
            }

            return ret;
        }

        public bool changeFiletype(string collection, string file, string type)
        {
            try{
            collectionHandler.getCollection(collection).getFile(file).Filetype = (FileType)Enum.Parse(typeof(FileType), type);
            }catch(ArgumentException){
                return false;
            }
            return true;
        }
        public string getMetadataContents(string collection, string file, string meta)
        {
            FileClass f = collectionHandler.getCollection(collection).getFile(file);
            return f.getMetadataContents(meta);
        }
        public List<FileClass> booleanAnd(List<FileClass> list1, List<FileClass> list2)
        {
            List<FileClass> list = new List<FileClass>();
            foreach (FileClass f in list1)
            {
                foreach (FileClass f2 in list2)
                {
                    if (f.Filename == f2.Filename) list.Add(f);
                }
            }
            return list;
        }
        public List<FileClass> booleanOr(List<FileClass> list1, List<FileClass> list2)
        {
            List<FileClass> list = new List<FileClass>();
            foreach (FileClass f in list1)
            {
                list.Add(f);
            }
            foreach (FileClass f2 in list2)
            {
                list.Add(f2);
            }
            return list;
        }
        public List<FileClass> booleanNot(List<FileClass> list1, List<FileClass> list2)
        {
            List<FileClass> list = new List<FileClass>();
            bool present = false;
            foreach (FileClass f in list1)
            {
                present = false;
                foreach (FileClass f2 in list2)
                {
                    if (f.Filename == f2.Filename) present = true;
                }
                if (!present) list.Add(f);
            }
            return list;
        }
        public List<string> getMetas(string collection, string file){
            return collectionHandler.getCollection(collection).getFile(file).getMetas();
        }
        #endregion

    }
    /// <summary>
    /// The program class represents the View
    /// </summary>
    public static class Program
    {
        public static Controller controller;
        public static View v;
        static void Main(string[] args)
        {
            controller = new Controller();
            v = new View();
        }
    }
}
